#include <windows.h>
#include <assert.h>

#include "widget.h"
#include "callback.h"
#include "delegate.h"

#ifdef BUILD_STATIC
extern int __main__(int,char**);
#endif

EXPORT widget_t *create_widget(widget_t *parent, int32_t x, int32_t y,
    uint32_t width, uint32_t height)
{
    DWORD style = WS_VISIBLE;
    HWND hwnd = NULL;
    HWND parenthwnd = NULL;
    // create memory for the widget
    widget_t *widget = malloc(sizeof(widget_t));
    // format the parent style
    if(parent == NULL) {
        style |= WS_POPUP;
        // calculate center coordinates if x or y is negative
        if(x<0) {
            uint32_t syswidth = GetSystemMetrics(SM_CXSCREEN);
            x = syswidth/2 - width/2;
        }
        if(y<0) {
            uint32_t sysheight = GetSystemMetrics(SM_CYSCREEN);
            y = sysheight/2 - height/2;
        }
    }
    else {
        style |= WS_CHILD;
        parenthwnd = parent->hwnd;
    }
    // set default value for the widget
    widget->parent = parent;
    widget->ext = NULL;
    widget->x = x;
    widget->y = y;
    widget->width = width;
    widget->height = height;
    widget->text = "";
    // create the window
    hwnd = CreateWindowEx(0,"WIDGET","",style,x,y,width,height,
        parenthwnd,NULL,GetModuleHandle(0),NULL);
    assert(hwnd != NULL);
    widget->hwnd = hwnd;
    // bind the handle with the widget
    callback_bind(hwnd, widget);
    // set the default callback functions
    widget->close = widget_close;
    widget->paint = widget_paint;
    widget->mouse_move = widget_mouse_move;
    widget->lbutton_down = widget_lbutton_down;
    widget->lbutton_up = widget_lbutton_up;
    widget->rbutton_down = widget_rbutton_down;
    widget->rbutton_up = widget_rbutton_up;
    widget->key_down = widget_key_down;
    widget->key_up = widget_key_up;
    // default function to call to free this widget
    widget->free = widget_free;
    // return a pointer to the widget
    return widget;
}

LRESULT CALLBACK widget_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
    switch(msg)
    {
        // user have closed this widget
        case WM_CLOSE:
        {
            widget_t *widget = callback_find_widget(hwnd);
            widget->close(widget);
        }
        break;
        // widget need to be repainted
        case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc;
            widget_t *widget = callback_find_widget(hwnd);

            hdc = BeginPaint(hwnd, &ps);
            widget->paint(widget, hdc);
            EndPaint(hwnd, &ps);
        }
        break;
        // user have press a key
        case WM_KEYDOWN:
        {
            widget_t *widget = callback_find_widget(hwnd);
            widget->key_down(widget, wparam);
        }
        break;
        // user have released a key
        case WM_KEYUP:
        {
            widget_t *widget = callback_find_widget(hwnd);
            widget->key_up(widget, wparam);
        }
        break;
        // move have been moves over this widget
        case WM_MOUSEMOVE:
        {
            widget_t *widget = callback_find_widget(hwnd);
            widget->mouse_move(widget,LOWORD(lparam), HIWORD(lparam),
                            wparam);
        }
        break;
        // user have pressed the left mouse button
        case WM_LBUTTONDOWN:
        {
            widget_t *widget = callback_find_widget(hwnd);
            widget->lbutton_down(widget, LOWORD(lparam), HIWORD(lparam),
                                 wparam);
        }
        break;
        // user have release the right mouse button
        case WM_LBUTTONUP:
        {
            widget_t *widget = callback_find_widget(hwnd);
            widget->lbutton_up(widget, LOWORD(lparam), HIWORD(lparam),
                               wparam);
        }
        break;
        // user have pressed the right mouse button
        case WM_RBUTTONDOWN:
        {
            widget_t *widget = callback_find_widget(hwnd);
            widget->rbutton_down(widget, LOWORD(lparam), HIWORD(lparam),
                                 wparam);
        }
        break;
        // user have release the right mouse button
        case WM_RBUTTONUP:
        {
            widget_t *widget = callback_find_widget(hwnd);
            widget->rbutton_up(widget, LOWORD(lparam), HIWORD(lparam),
                               wparam);
        }
        break;
        // mouse pointer have left the widgets client area
        case WM_MOUSELEAVE:
        {
            widget_t *widget = callback_find_widget(hwnd);
            widget->mouse_leave(widget);
        }
        break;
        // this proc did not handle this message, let the default proc.
        // the other messages
        default:
            return DefWindowProc(hwnd, msg, wparam, lparam);
    }
    return 0;
}

#ifdef BUILD_DLL
EXPORT int _WinMain(HINSTANCE inst, HINSTANCE preinst, LPSTR cmdline,
             INT cmdshow, int (*main_ptr)(int,char**))
#else
int WINAPI WinMain(HINSTANCE inst, HINSTANCE preinst, LPSTR cmdline,
                   INT cmdshow)
#endif
{
    // register the widget class
    WNDCLASSEX wc;
    ZeroMemory(&wc, sizeof(WNDCLASSEX));
    wc.cbSize        = sizeof(WNDCLASSEX);    
    wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND);
    wc.lpszMenuName  = 0;
    wc.style         = CS_OWNDC;
    wc.lpfnWndProc   = widget_proc;
    wc.hInstance     = GetModuleHandle(0);
    wc.hIcon         = 0;
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.lpszClassName = "WIDGET";
    RegisterClassEx(&wc);

    // initialize callback mapping (128 ~= max number of widget used)
    callback_alloc(128);
    delegate_alloc(128);
    // call the main window function
    #ifdef BUILD_DLL
    main_ptr(__argc, __argv);
    #elif BUILD_STATIC
    __main__(__argc, __argv);
    #endif
    
    MSG msg;
    // handler incomming messages to the application
    while(GetMessage(&msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    // free memory for the widgets and callback function
    callback_free();
    delegate_free();

    // all done, return an error code if applicable
    return msg.wParam;
}

void widget_free(widget_t *self)
{
    free(self);
}

void widget_close(widget_t *self)
{
    PostQuitMessage(0);
}

void widget_paint(widget_t *self, HDC hdc)
{
}

void widget_mouse_move(widget_t *self, int32_t x, int32_t y, uint32_t flag)
{
}

void widget_lbutton_down(widget_t *self, int32_t x, int32_t y, uint32_t flag)
{
}

void widget_lbutton_up(widget_t *self, int32_t x, int32_t y, uint32_t flag)
{
}

void widget_rbutton_down(widget_t *self, int32_t x, int32_t y, uint32_t flag)
{
}

void widget_rbutton_up(widget_t *self, int32_t x, int32_t y, uint32_t flag)
{
}

void widget_key_down(widget_t *self, uint32_t key)
{
}

void widget_key_up(widget_t *self, uint32_t key)
{
}

void widget_mouse_leave(widget_t *self)
{
}

